package com.imooc.business.core.executor.calculate;

import com.imooc.business.context.DataContext;
import com.imooc.business.domain.Calculate;
import com.imooc.business.domain.Metadata;
import com.imooc.business.domain.Rule;
import com.imooc.module.collect.enums.FunctionEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author Tim
 * @Description:
 * @Version 1.0
 */
@Slf4j
@Component
public class DefaultCalculateProcess implements CalculateProcess{

    private final List<ExpressionProcess> expressionProcesses;
    public DefaultCalculateProcess(List<ExpressionProcess> expressionProcesses) {
        this.expressionProcesses = expressionProcesses;
    }
    @Override
    public void process(DataContext context) {
        DataContext.CacheModel cacheModel = context.getCacheModel();
        Rule rule = cacheModel.getRule();

        List<Calculate> calculates = rule.getCalculates();
        if (CollectionUtils.isEmpty(calculates)) {
            log.warn("计算指标数据没有找到, ruleId:{}, businessId:{}, collectDate:{}", rule.getRuleId(), rule.getBusinessId(), context.getRequest().getRequestDate());
            return;
        }
        //方便我们获取对应指标的metaData数据
        List<Metadata> metadatas = rule.getMetadatas();
        Map<String, Metadata> metadataMap = metadatas.parallelStream().collect(Collectors.toMap(Metadata::getEnName, v -> v));

        List<Map<String, Object>> results = cacheModel.getResults();
        results.parallelStream().forEach(result -> {
            // 每一个calculate模型都用来计算一个新的指标，
            //所以所有的结果数据都要被一个新的计算模型计算一次，以此来计算对应结果数据下的calculate的指标结果
            calculates.parallelStream().forEach(calculate -> {
                calculateMetaDataValue(metadataMap, result, calculate);
            });
        });
    }

    private void calculateMetaDataValue(Map<String, Metadata> metadataMap, Map<String, Object> result, Calculate calculate) {
        Metadata metadata = metadataMap.get(calculate.getEnName());
        Class<?> javaType = metadata.getDataType().getJavaType();
        String expression = calculate.getExpression();
        Map<String, Object> parameters = buildCalculateParams(calculate, result);

        Object calculateValue;
        if (CollectionUtils.isNotEmpty(expressionProcesses) && !parameters.isEmpty()) {
            calculateValue = expressionProcesses.get(0).process(javaType, expression, parameters, metadata.getDefaultValue());
        } else {
            calculateValue = metadata.getDefaultValue();
        }
        result.put(calculate.getEnName(), calculateValue);
    }

    private Map<String, Object> buildCalculateParams(Calculate calculate, Map<String, Object> result) {
        Map<String, Object> params = new HashMap<>();
        params.putAll(result);
        if (FunctionEnum.AVG.getFunName().equals(calculate.getFunc())) {
            params.put("count", calculate.getParameters().size());
        }
        return params;
    }
}
